// TossimObserverSwarm
// by Mina Jung @ syracuse university
// April 2010

#import "TossimObserverSwarm.h"
#import "TossimModelSwarm.h"
#import <collections.h>
#import <objectbase.h>
#import <analysis.h>
#import <gui.h>

@implementation TossimObserverSwarm

// createBegin: here we set up the default observation parameters.

+ createBegin: aZone
{
  TossimObserverSwarm *obj;
  id <ProbeMap> probeMap;
  
  // Superclass createBegin to allocate ourselves.

  obj = [super createBegin: aZone];

  // Fill in the relevant parameters (only one, in this case).

  obj->displayFrequency = 1;
  obj->drawBatteryGraph = 1;
  obj->drawCurrentGraph = 1;
  obj->drawRadioCurrentGraph = 1;
  obj->drawMessageGraph = 1;
  obj->drawCurrentHistogram = 1;

  obj->timeSecond = 0;


  // build a customized probe map. 


  probeMap = [EmptyProbeMap createBegin: aZone];
  [probeMap setProbedClass: [self class]];
  probeMap = [probeMap createEnd];




  [probeMap addProbe: [probeLibrary getProbeForVariable: "drawBatteryGraph"
                                    inClass: [self class]]];

  [probeMap addProbe: [probeLibrary getProbeForVariable: "drawCurrentGraph"
                                    inClass: [self class]]];

  [probeMap addProbe: [probeLibrary getProbeForVariable: "drawRadioCurrentGraph"
                                    inClass: [self class]]];

  [probeMap addProbe: [probeLibrary getProbeForVariable: "drawMessageGraph"
                                    inClass: [self class]]];

  [probeMap addProbe: [probeLibrary getProbeForVariable: "drawCurrentHistogram"
                                    inClass: [self class]]];


  [probeMap addProbe: [probeLibrary getProbeForVariable: "displayFrequency"
                                    inClass: [self class]]];
  // install custom probeMap into the probeLibrary.

  [probeLibrary setProbeMap: probeMap For: [self class]];

printf("\nObserver -- after probemap creation!");

  return obj;
}


- createEnd
{
  return [super createEnd];
}

- _worldRasterDeath_ : caller
{
  [worldRaster drop];
  worldRaster = nil;
  return self;
}

- _batteryGraphDeath_ : caller
{
  [batteryGraph drop];
  batteryGraph = nil;
  return self;
}

- _currentGraphDeath_ : caller
{
  [currentGraph drop];
  currentGraph = nil;
  return self;
}

- _radioCurrentGraphDeath_ : caller
{
  [radioCurrentGraph drop];
  radioCurrentGraph = nil;
  return self;
}

- _msgGraphDeath_ : caller
{
  [msgGraph drop];
  msgGraph = nil;
  return self;
}

- _currentHistogramDeath_ : caller
{
  [currentHistogram drop];
  currentHistogram = nil;
  return self;
}


- buildObjects
{
  int i,j;

  [super buildObjects];

  CREATE_ARCHIVED_PROBE_DISPLAY (self); // <-- observer
  
//[controlPanel setStateStopped];

  // create the model 
  // subswarm of the observer. 

printf("\nObserver -- BuildObject In");

  tossimModelSwarm = [TossimModelSwarm create: self];

printf("\nObserver -- ModelSwarm Created");
  
  // create probe objects on the model  

  CREATE_ARCHIVED_PROBE_DISPLAY (tossimModelSwarm); // <-- model 
 

  // default state (PrcCtrl widget) - stopped

  [controlPanel setStateStopped];


//------------------------------------------------------------
//                        MODEL 
//------------------------------------------------------------


  [tossimModelSwarm buildObjects];


//----------------------- Color Resources --------------------
  // create a colormap
  colormap = [Colormap create: self];


  [colormap setColor: 0 ToName: "white"];    // active

  for (j=1, i = 149; i > 0; i--,j++)
    [colormap setColor: j ToRed: (double)i / 150.0 Green: (double)i / 150.0 Blue: 0];

  [colormap setColor: 150 ToName: "red"];    // active

  [colormap setColor: 151 ToName: "white"];  // not active

  [colormap setColor: 152 ToName: "blue"];   // die

  [colormap setColor: 153 ToName: "gray"];  // for wall

  // set all motes' colors to white <<--- not activated yet

//  [[tossimModelSwarm getSensorList] forEach: M(setSensorColor:) : (id) 151];
  

//----------------------- Displays... (World Raster window)

  //  create a 2d window for display, set its size, zoom factor, title.
  
  worldRaster = [ZoomRaster createBegin: self];
  SET_WINDOW_GEOMETRY_RECORD_NAME (worldRaster);
  worldRaster = [worldRaster createEnd];
  [worldRaster enableDestroyNotification: self
               notificationMethod: @selector (_worldRasterDeath_:)];
  [worldRaster setColormap: colormap];
  [worldRaster setZoomFactor: 4];
  [worldRaster setWidth: [[tossimModelSwarm getWorld] getSizeX]
	       Height: [[tossimModelSwarm getWorld] getSizeY]];
  [worldRaster setWindowTitle: "Wireless Sensor Network with TOSSIM"];
  [worldRaster pack];				  // draw the window.

  // create a Value2dDisplay --> display life mote all motes on the worldRaster widget
  // white <-- not activate yet
  // red <-- alive (active)
  // blue   <-- die


  envDisplay = [Value2dDisplay createBegin:self];
  [envDisplay setDisplayWidget: worldRaster colormap: colormap];
  [envDisplay setDiscrete2dToDisplay: [[tossimModelSwarm getSensorSpace] getEnvironmentValues]];

    //[sensorDisplay setDisplayMappingM: 1 C: 0];	  // 
  envDisplay = [envDisplay createEnd];


  // create an Object2dDisplay --> display mote itself on the worldRaster widget
  // also receives probes.


  moteDisplay = [Object2dDisplay createBegin: self];
  [moteDisplay setDisplayWidget: worldRaster];
  [moteDisplay setDiscrete2dToDisplay: [[tossimModelSwarm getSensorSpace] getSensorGrid]];
  [moteDisplay setObjectCollection: [tossimModelSwarm getSensorList]];
  [moteDisplay setDisplayMessage: M(drawSelfOn:)];
  moteDisplay = [moteDisplay createEnd];
 
 
  // tell the world raster to send mouse clicks to the moteDisplay
  // this allows the user to right-click on the display to probe the motes.

  [worldRaster setButton: ButtonRight
               Client: moteDisplay
               Message: M(makeProbeAtX:Y:)];

//                        Graph Displays 

  // Create the graph widget to display battery amount upto now..
  // later histogram graphs would be addes...

  if (drawBatteryGraph) {
    batteryGraph = [EZGraph createBegin: self];
    SET_WINDOW_GEOMETRY_RECORD_NAME (batteryGraph);
    [batteryGraph setTitle: "Battery Capacity over time"];
    [batteryGraph setAxisLabelsX: "time" Y: "remaining battery capacity (mAh)"];
    batteryGraph = [batteryGraph createEnd];

    [batteryGraph enableDestroyNotification: self
                notificationMethod: @selector (_batteryGraphDeath_:)];

    [batteryGraph createAverageSequence: "avg. bat"
                         withFeedFrom: [tossimModelSwarm getSensorList] 
                          andSelector: M(getAmtBattery)];

    [batteryGraph createMaxSequence: "max. bat"
                         withFeedFrom: [tossimModelSwarm getSensorList] 
                          andSelector: M(getAmtBattery)];

    [batteryGraph createMinSequence: "min. bat"
                         withFeedFrom: [tossimModelSwarm getSensorList] 
                          andSelector: M(getAmtBattery)];

  }


  if (drawCurrentGraph) {
    currentGraph = [EZGraph createBegin: self];
    SET_WINDOW_GEOMETRY_RECORD_NAME (currentGraph);
    [currentGraph setTitle: "Current Usage per Second"];
    [currentGraph setAxisLabelsX: "time" Y: "used current (mA)"];
    currentGraph = [currentGraph createEnd];

    [currentGraph enableDestroyNotification: self
                notificationMethod: @selector (_currentGraphDeath_:)];

    [currentGraph createAverageSequence: "avg. total"
                         withFeedFrom: [tossimModelSwarm getSensorList] 
                          andSelector: M(getSecCurrent)];

    [currentGraph createMinSequence: "min. total"
                         withFeedFrom: [tossimModelSwarm getSensorList] 
                          andSelector: M(getSecCurrent)];

    [currentGraph createMaxSequence: "max. total"
                         withFeedFrom: [tossimModelSwarm getSensorList] 
                          andSelector: M(getSecCurrent)];

    [currentGraph createAverageSequence: "cpu"
                         withFeedFrom: [tossimModelSwarm getSensorList] 
                          andSelector: M(getSecCurrentC)];

    [currentGraph createAverageSequence: "radio"
                         withFeedFrom: [tossimModelSwarm getSensorList] 
                          andSelector: M(getSecCurrentR)];

    [currentGraph createAverageSequence: "leds"
                         withFeedFrom: [tossimModelSwarm getSensorList] 
                          andSelector: M(getSecCurrentL)];

  }

  if (drawRadioCurrentGraph) {
    radioCurrentGraph = [EZGraph createBegin: self];
    SET_WINDOW_GEOMETRY_RECORD_NAME (radioCurrentGraph);
    [radioCurrentGraph setTitle: "Radio Usage per Second"];
    [radioCurrentGraph setAxisLabelsX: "time" Y: "used current (mA)"];
    radioCurrentGraph = [radioCurrentGraph createEnd];

    [radioCurrentGraph enableDestroyNotification: self
                notificationMethod: @selector (_radioCurrentGraphDeath_:)];

    [radioCurrentGraph createAverageSequence: "avg"
                         withFeedFrom: [tossimModelSwarm getSensorList] 
                          andSelector: M(getSecCurrentR)];

    [radioCurrentGraph createMinSequence: "min"
                         withFeedFrom: [tossimModelSwarm getSensorList] 
                          andSelector: M(getSecCurrentR)];

    [radioCurrentGraph createMaxSequence: "max"
                         withFeedFrom: [tossimModelSwarm getSensorList] 
                          andSelector: M(getSecCurrentR)];
  }

  if (drawMessageGraph) {
    msgGraph = [EZGraph createBegin: self];
    SET_WINDOW_GEOMETRY_RECORD_NAME (msgGraph);
    [msgGraph setTitle: "Message Communication over time"];
    [msgGraph setAxisLabelsX: "time" Y: "avg. number of msgs"];
    msgGraph = [msgGraph createEnd];

    [msgGraph enableDestroyNotification: self
                notificationMethod: @selector (_msgGraphDeath_:)];

    [msgGraph createAverageSequence: "sent"
                         withFeedFrom: [tossimModelSwarm getSensorList] 
                          andSelector: M(getSentMsgsNow)];

    [msgGraph createAverageSequence: "recv"
                         withFeedFrom: [tossimModelSwarm getSensorList] 
                          andSelector: M(getRecvMsgsNow)];
  }

  if (drawCurrentHistogram) {
    currentHistogram = [EZBin createBegin: self];
    SET_WINDOW_GEOMETRY_RECORD_NAME (currentHistogram);
    [currentHistogram setTitle: "Battery"];
    [currentHistogram setAxisLabelsX: "Capacity" Y: "# of Motes"];
    [currentHistogram setBinCount: 5];
    [currentHistogram setLowerBound: 0];
    [currentHistogram setUpperBound: 2000];
    [currentHistogram setCollection: [tossimModelSwarm getSensorList]];
    [currentHistogram setProbedSelector: M(getAmtBattery)];
//    [currentHistogram setProbedSelector: M(getTotCurrentC)];
//    [currentHistogram setProbedSelector: M(getTotCurrentR)];
//    [currentHistogram setProbedSelector: M(getTotCurrentL)];
    currentHistogram = [currentHistogram createEnd];

    [currentHistogram enableDestroyNotification: self
                notificationMethod: @selector (_currentHistogramDeath_:)];



  }
  return self;
}  

- _updateHistogram_
{
  if (currentHistogram) {
    [currentHistogram reset];
    [currentHistogram update];
//    [currentHistogram getAverage];
    [currentHistogram output];
  }

  return self;
}

- _update_
{
timeSecond++;

printf("\nObserver - _update_[%5d]", timeSecond);

  //+++++++++++++++++++++++++++++++++++++++++++++++++
  //       Run Tossim Activity with discrete Time
  //+++++++++++++++++++++++++++++++++++++++++++++++++

  [tossimModelSwarm updateTossim: timeSecond];

  //+++++++++++++++++++++++++++++++++++++++++++++++++

  if (worldRaster)
  {
    if (envDisplay) {
      [envDisplay display];
    }

    if (moteDisplay) {
      [moteDisplay display];
    }

    [worldRaster drawSelf];

  }

  return self;
}


- buildActions
{
  [super buildActions];
  

  [tossimModelSwarm buildActions];
  
  
  displayActions = [ActionGroup create: self];


  [displayActions createActionTo: self message: M(_update_)];

  if (drawBatteryGraph) {
    [displayActions createActionTo: batteryGraph message: M(step)];
  }

  if (drawCurrentGraph) {
    [displayActions createActionTo: currentGraph message: M(step)];
  }

  if (drawRadioCurrentGraph) {

    [displayActions createActionTo: radioCurrentGraph message: M(step)];
  }

  if (drawMessageGraph) {
    [displayActions createActionTo: msgGraph message: M(step)];
  }

  if (drawCurrentHistogram) {
    [displayActions createActionTo: self message: M(_updateHistogram_)];
  }

  [displayActions createActionTo: probeDisplayManager message: M(update)];


  [displayActions createActionTo: actionCache message: M(doTkEvents)];

  // note frequency!
  displaySchedule = [Schedule create: self setRepeatInterval: displayFrequency];
  
  [displaySchedule at: 0 createAction: displayActions];
  
  return self;
}  


- (id <Activity>)activateIn:  swarmContext
{

  [super activateIn: swarmContext];


  [tossimModelSwarm activateIn: self];


  [displaySchedule activateIn: self];
  

  return [self getActivity];
}


@end
